#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<string.h>
#include<stdlib.h>
#define SIZE 1024
#define LABLE "# "
#define DELIM " \t"

extern char** environ;


char pwd[SIZE];

char* getname()
{
    return getenv("USER");
}

char* gethostname()
{
    return getenv("HOSTNAME");
}

//获取环境变量的PWD
char* getpwd()
{
    return getenv("PWD");
}

//获取当前地址PWD
void getPWD()
{
    getcwd(pwd,sizeof(pwd));
}

char commandline[SIZE];

void Interact(char* cline,int size)
{
    printf("[%s@%s %s]"LABLE,getname(),gethostname(),getpwd()); 
    
    //scanf("%s",commandline);//这里不用scanf是因为sancf输入空格会结束
    char *s = fgets(cline,size,stdin);
    (void)s;//防止在releas版本时，不使用的变量或者指针会被优化报错，用void强转抵消报错
    //fgets 函数在读取输入时，如果遇到换行符 \n（即回车产生的字符），会将其写入字符串中，并在末尾自动添加 \0 作为字符串结束标志。
    
    cline[strlen(cline)-1]='\0';
    //这里就是把最后的\n置为\0
    //这是 fgets 与 scanf 处理输入的核心差异之一（fgets 保留 \n，scanf 不保留） 
}

int splitstring(char* cline,char* _argv[])
{
    int i=0;
    //首次调用时，传入待分割的原始字符串（如 char[] 数组）；
    //后续调用时，传入 NULL，表示继续分割上一次未处理完的字符串。
    _argv[i++]=strtok(cline,DELIM);
    //当 strtok 返回 NULL 时，赋值表达式 argv[i++] = NULL 的结果为 NULL（C 中 NULL 等价于 0），此时 while 循环终止。
    while(_argv[i++]=strtok(NULL,DELIM));
    return i-1;
}

void NormalExcute(char* _argv[])
{
    pid_t id=fork();
    if(id<0)
    {
        perror("fork");
        return;
    }
    else if(id==0)
    {
        //子进程
        //execvpe(_argv[0],_argv,environ);
        execvp(_argv[0],_argv);
        exit(44);
    }
    else
    {
        int stasus=0;
        //父进程
        pid_t ptr=waitpid(id,&stasus,0);

        if(ptr==id)
        {
            int lastcode=WEXITSTATUS(stasus);
            printf("%d\n",lastcode);
        }
    }
}

int buildCommand(char* _argv[],int _argc)
{
    if(_argc==2 && strcmp(_argv[0],"cd")==0)
    {
        //chdir 仅修改内核态的工作目录，用户态的 PWD 等环境变量需显式调用 setenv 或 putenv 更新。
        chdir(_argv[1]);
        //获取cd后的内核PWD
        getPWD(); 
        //更新cd后的用户界面的环境变量表PWD
        sprintf(getenv("PWD"),"%s",pwd);

        return 1;
    }

    //给ls命令加颜色
    if(strcmp(_argv[0],"ls")==0)
    {
        _argv[_argc++]="--color";
        _argv[_argc]=NULL;
    }

    return 0;
}

int main()
{

    char* argv[32];
    while(1)
    {
        //2.交互问题，获取命令行
        //把结尾的注释优化为接口
        Interact(commandline,sizeof(commandline));

        //3.字串分割的问题，解析命令行 
        int argc=splitstring(commandline,argv);
        if(argc==0) continue;
        //打印查看argv[]命令
        //for(int i=0;argv[i];i++) 
        //{
        //    printf("[%d]:%s\n",i,argv[i]);
        //}
        //printf("echo:""%s\n",commandline);
        

        //4.指令的判断(是否是内建命令)
        //为什么要做这一步呢   如cd自建命令，在子进程中使用父进程的环境变量pwd不会改变
        int a=buildCommand(argv,argc);
        

        //5.普通命令的执行
        if(!a)
        {
            NormalExcute(argv);
        }
    }



    //printf("[%s@%s %s]"LABLE,getname(),gethostname(),getpwd()); 
    //
    ////scanf("%s",commandline);
    //fgets(commandline,sizeof(commandline),stdin);
    ////fgets 函数在读取输入时，如果遇到换行符 \n（即回车产生的字符），会将其写入字符串中，并在末尾自动添加 \0 作为字符串结束标志。
    //
    //commandline[strlen(commandline)-1]='\0';
    ////这里就是把最后的\n置为\0
    ////这是 fgets 与 scanf 处理输入的核心差异之一（fgets 保留 \n，scanf 不保留） 
    //printf("echo:""%s\n",commandline);
    return 0;
}
